﻿
using System;
using Rekx.Graphics;
using OpenTK.Graphics;
using OpenTK.Math;
using System.Drawing;
using Rekx;



namespace Rekx.Pong
{
     
    /// <summary>
    /// This is the main window of the game - all interaction goe through this
    /// </summary>
    class PongWindow : OpenTK.GameWindow
    {
        #region Member variables
        // there will be a lot of temp variables here - they will be refacatored out
           

        Rekx.Graphics.GraphicsDevice graphicsDevice = null;
        Paddle topPaddle;
        Paddle bottomPaddle;
        Ball ball;
        LevelComponents.Floor floor;
        LevelComponents.Wall leftWall;
        LevelComponents.Wall rightWall;
        int point1 = 0;
        int point2 = 0;

        bool userControlled = false;

        Camera camera;
        double angleXZ = 0;
        double angleYZ = 0;
        

        #endregion


        /// <summary>
        /// Constructor of the game
        /// </summary>
        public PongWindow() : 
            base(800, 600) // Calls the base class (the one it inherits from) and creates the window in 800x600
        {     
            // Initializing done here
            graphicsDevice = new Rekx.Graphics.GraphicsDevice();
            camera = new Camera(800/600, 0.1f, 100.0f, 45, new Vector3(0,0,26), new Vector3());
            graphicsDevice.Camera = camera;

            // Must be initialized after the GraphicsDevice because it uses vram
            topPaddle = new Paddle(9.5f, 2.0f);
            topPaddle.DrawComponent.Texture = new Texture(Defines.DATADIR + "paddle.jpg");
            
            bottomPaddle = new Paddle(-9.5f, 2.0f);
            bottomPaddle.DrawComponent.Texture = new Texture(Defines.DATADIR + "paddle.jpg");

            ball = new Ball();         
            

            floor = new LevelComponents.Floor();
            floor.DrawComponent.Texture = new Texture(Defines.DATADIR + "levelfloor.jpg");
            floor.DrawComponent.Effect = graphicsDevice.CreateEffect(Defines.DATADIR + "fragmentlight.cgfx");

            leftWall = new LevelComponents.Wall(new Vector3(-11.0f, 0.0f, 0.0f), 2.0f, 20.0f);
            rightWall = new LevelComponents.Wall(new Vector3(11.0f, 0.0f, 0.0f), 2.0f, 20.0f);
        }

        public override void OnRenderFrame(OpenTK.RenderFrameEventArgs e)
        {
            base.OnRenderFrame(e);
                        

            // Draw the scene's content.
            DrawLevel();
            DrawUnits();


            // Draw the scene
            graphicsDevice.DrawScene((float)e.Time);

            // Draw velocity and position of ball
            graphicsDevice.DrawString(ball.Position.ToString(), new OpenTK.Math.Vector2(400, 0));
            graphicsDevice.DrawString(ball.velocity.ToString(), new OpenTK.Math.Vector2(0, 0));

            // Draw scores
            graphicsDevice.DrawString(point1.ToString(), new OpenTK.Math.Vector2(400, 80));
            graphicsDevice.DrawString(point2.ToString(), new OpenTK.Math.Vector2(400, 550));

            // Draw toggle message
            graphicsDevice.DrawString("Press space to control the lower paddle!", new OpenTK.Math.Vector2(140, 575));

            
            // Swap the buffers to the screen. I.e. present the stuff!
            SwapBuffers();
        }

        private void DrawUnits()
        {
            // Draw the paddles.
            graphicsDevice.Draw(topPaddle.DrawComponent, 
                Matrix4.Translation(topPaddle.PositionX, 0.0f, -10.25f));
            graphicsDevice.Draw(bottomPaddle.DrawComponent, 
                Matrix4.Translation(bottomPaddle.PositionX, 0.0f, 10.25f));
            
            // Draw the ball.
            graphicsDevice.Draw(ball.DrawComponent, Matrix4.Translation(ball.Position));
 
        }

        private void DrawLevel()
        {
            // Draw the floor.    
            graphicsDevice.Draw(floor.DrawComponent, Matrix4.Translation(new Vector3()));


            // Draw the walls  
            graphicsDevice.Draw(leftWall.DrawComponent, Matrix4.Translation(leftWall.Position));
            graphicsDevice.Draw(rightWall.DrawComponent, Matrix4.Translation(rightWall.Position));
   
        }


        protected override void OnResize(OpenTK.Platform.ResizeEventArgs e)
        {
            base.OnResize(e);

            graphicsDevice.Width = e.Width;
            graphicsDevice.Height = e.Height;
        }
        

        public override void OnUpdateFrame(OpenTK.UpdateFrameEventArgs e)
        {
            base.OnUpdateFrame(e);


            UpdateInput(e.Time);

            HandleCollisions();

            ball.Update((float)e.Time);
            

            
        }

        private void HandleCollisions()
        {
            // collide with top
            if (ball.Position.Z <= -9.75)
            {
                if (topPaddle.CollidesWithBall(ball))
                {
                    if (ball.velocity.Z < 0)
                    {
                        ball.velocity.Z = -ball.velocity.Z;
                    }
                }
                else
                {
                    point2++;
                    ball.Restart();
                }
            }
            // collide with bottom
            if (ball.Position.Z >= 9.75)
            {
                if (bottomPaddle.CollidesWithBall(ball))
                {
                    if (ball.velocity.Z > 0)
                    {
                        ball.velocity.Z = -ball.velocity.Z;
                    }
                }
                else
                {
                    point1++;
                    ball.Restart();
                    ball.velocity.Z *= -1;
                }
            }


            // collide with left wall
            if (ball.Position.X <= -9.75)
            {
                if (ball.velocity.X < 0)
                    ball.velocity.X = -ball.velocity.X;
            }

            // collide with right wall
            if (ball.Position.X >= 9.75)
                if (ball.velocity.X > 0)
                    ball.velocity.X = -ball.velocity.X;
        }

        private void UpdateInput(double ms)
        {
            // quit on escape
            if (Keyboard[OpenTK.Input.Key.Escape])
                Exit();

            if (Keyboard[OpenTK.Input.Key.Space])
                userControlled = true;

            if (userControlled)
            {
                // move paddle
                if (Keyboard[OpenTK.Input.Key.Left])
                    bottomPaddle.MoveLeft();

                if (Keyboard[OpenTK.Input.Key.Right])
                    bottomPaddle.MoveRight();
            }
            else
            {
                bottomPaddle.MoveSelfAccordingToBall(ball);

            }
            topPaddle.MoveSelfAccordingToBall(ball);

            // mouse clicked? Move camera
            if (Mouse[OpenTK.Input.MouseButton.Left])
            {
                
                MoveCameraCicleXZ(Mouse.XDelta*(float)ms);
                MoveCameraCicleYZ(Mouse.YDelta * (float)ms);
            }
        }

        public void MoveCameraCicleXZ(float ms)
        {
            Vector2 currentEye = new Vector2(camera.Eye.X, camera.Eye.Z);
            float length = currentEye.Length;

            currentEye.X = (float) Math.Cos(angleXZ);
            currentEye.Y = (float) Math.Sin(angleXZ);

            currentEye *= length;
            camera.Eye = new Vector3(currentEye.X, camera.Eye.Y, currentEye.Y);

            angleXZ = angleXZ + 0.1f*ms;

        }

        public void MoveCameraCicleYZ(float ms)
        {
            Vector2 currentEye = new Vector2(camera.Eye.Y, camera.Eye.Z);
            float length = currentEye.Length;

            currentEye.X = (float) Math.Cos(angleYZ);
            currentEye.Y = (float)Math.Sin(angleYZ);

            currentEye *= length;
            camera.Eye = new Vector3(camera.Eye.X, currentEye.X, currentEye.Y);

            angleYZ = angleYZ + 0.1f * ms;

        }

        public override void OnUnload(EventArgs e)
        {


            // clean up! finally added
            graphicsDevice.Dispose();
            topPaddle.DrawComponent.Dispose();
            bottomPaddle.DrawComponent.Dispose();
            ball.DrawComponent.Dispose();
            leftWall.DrawComponent.Dispose();
            rightWall.DrawComponent.Dispose();
            floor.DrawComponent.Dispose();

            base.OnUnload(e);


        }

        #region MAIN - PROGRAM ENTRY

       
        /// <summary>
        /// Application entry
        /// </summary>
        /// <param name="args">Command line argument</param>
        private static void Main(string[] args)
        {
            // using statement because it makes pongWindow.Dispose called when out of scope
            using (PongWindow pongWindow = new PongWindow())
            {
                // Run the application, i.e. enter game loop
                pongWindow.Run(60.0, 120);
                
            }
           
        }

        #endregion 
    }
}
